/* synth.h -  Copyright (C) 2014 James Boyle

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
#ifndef SYNTH_HEADER_INCLUDED
#define SYNTH_HEADER_INCLUDED

#include "asciiboard_const.h"

/* the size of buffer to store transmitted float, note that a bit 32 float 
   will have around 7 significant digits */
#define VAL_BUFFER_SIZE 16
#define END_OF_LIST -1
#define INVALID_OSC -1

/* rw is a set of macros describing the read / write status of the synth, */
enum rw { READ = 0,
          WRITE_WAVE_ONE = RW_WAVE_ONE_CODE,
          WRITE_ENVELOPE_ONE = RW_ENVELOPE_ONE_CODE,
          WRITE_MAP_ONE = RW_MAP_ONE_CODE,
          WRITE_WAVE_TWO = RW_WAVE_TWO_CODE,
          WRITE_ENVELOPE_TWO = RW_ENVELOPE_TWO_CODE,
          WRITE_MAP_TWO = RW_MAP_TWO_CODE };
enum tone_mode { TONE_OFF = -1, TONE_NO_SOUND = MAX_POLYPHONY };

typedef enum mode { ADD, AM, FM } MODE;

typedef struct oscillator
{
	float twopiovrsr; /* 2pi / samplerate */
	float curr_freq, curr_phase, incr;
} OSCILLATOR;

typedef struct table_osc
{
	OSCILLATOR osc;
	int env_len, env_index, env_hold;
	float *table, *env;
	float table_len, sizeovsr, env_val, env_incr;
} TABLE_OSC;

typedef struct tone_node
{
	/* there are 26 tone_data, from 0 to 25 -> 'a' to 'z' */
	char tone_code; 
	/* the tone may either be off (state = TONE_OFF), or active
	(state = #oscillator ie 0 <= state < MAX_POLYPHONY), or active but not 
	sounding (state = TONE_NO_SOUND) */
	int state;
	/* links needed for a doubly linked list which keeps track of the next
	   and most recent sounding tone, if the link is invalid then
	   it will be set to END_OF_LIST.  These are indexes to the position
	   of the next and previous elements - an array is fine for this list
	   as it is very small and a constant size. */
	int next_sounding, prev_sounding;
	
} TONE_NODE;

/* tone_manager tracks the tones that are currently playing, and the
   state of the oscillators */
typedef struct tone_manager
{
	/* maintain an array of all tones, where the tones are linked in a
	  list to record which tones are sounding and the order in which
	  they were played */
	TONE_NODE tones[TONE_COUNT]; 
	/* keep track of the start and end of the list, with the first sounding
	   at the start of the list, and the most last sounding tone at the end */
	int first_sounding, last_sounding;
	/* keep track of the number of tones playing */
	int sounding_count;
	
	/* an array of available oscillators, maintained as a simple stack */
	int available_osc[MAX_POLYPHONY];
	/* a set of oscillators, one for every possible tone and channel */
	TABLE_OSC out[CHANS][MAX_POLYPHONY];

	int envelopes_synced, silent;
} TONE_MANAGER;

/* synth contains all of the synthesiser internal data */
typedef struct synth
{
	/* storage and buffers the synth uses */
	float envelope[CHANS][ENVELOPE_MAX_SIZE];
	float tone_map[CHANS][TONE_COUNT];
	float *audio_buffer;
	/* the wave table contains an additional "guard point", which avoids
	   a conditional being run every time the wave table is used */
	float wave_table[CHANS][WAVE_SIZE+1];

	/* flags and variables monitoring the state of the synth */
	int envelope_len[CHANS], write_pos;
	int rw, silent, val_pos;
	int audio_buffer_size;
	char val_buffer[VAL_BUFFER_SIZE];
	TONE_MANAGER tone_data;
	MODE mix_mode;

	void (*audio_func)(const float *, const int, void *);
	void *audio_func_args;
} SYNTH;

void synth_init(SYNTH *const s, float *audio_buffer, const int buffer_size,
                void (*af)(const float *, const int, void *), void *af_args);
void synth_reset(SYNTH *const s);
int synth_process_ch(SYNTH *const s, const char ch);
void synth_wave_copy(SYNTH *const s, const float *new_wave,
                     const int chan);
int synth_envelope_copy(SYNTH *const s, const int size, const float *new_env,
                        const int chan);
void synth_map_copy(SYNTH *const s, const float *new_map, const int chan);
#ifdef DEBUG
void synth_tone_debug(const TONE_MANAGER *const tm);
#endif

#endif
